/**
 * $version:  0.1 
 * $Date: 2011-01-21 
 *
 * Copyright (C) 2010-2011 Jawa Software. All rights reserved.
 *
 */

package org.jawa.user;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.jawa.branch.Branch;
import org.jawa.branch.BranchManager;
import org.jawa.core.cache.CacheSizes;
import org.jawa.core.cache.Cacheable;
import org.jawa.core.cache.CannotCalculateSizeException;
import org.jawa.core.database.DbConnectionManager;
import org.jawa.core.exception.NotFoundException;
import org.jawa.core.log.Log;
import org.jawa.user.event.UserEventDispatcher;

/**
 * 用户信息 .
 */
public class User implements Cacheable {
	private static final long serialVersionUID = -8557878791634494271L;

	private static final String LOAD_PROPERTIES = "SELECT propname, propvalue FROM app_user_prop WHERE user_id=?";
	private static final String DELETE_PROPERTY = "DELETE FROM app_user_prop WHERE user_id=? AND propname=?";
	private static final String UPDATE_PROPERTY = "UPDATE app_user_prop SET propvalue=? WHERE user_id=? AND propname = ?";
	private static final String INSERT_PROPERTY = "INSERT INTO app_user_prop (user_id, propname, propvalue) VALUES (?, ?, ?)";

	private Long userId;
	private Long brId;
	private String loginNo;//用户名
	private String userType;
	private String userName;//姓名
	private String userSex;
	private String userStatus;
	private String userPassword;
	private String userPicture;
	private String userNation;
	private String userPlace;
	private String userPolity;
	private String userEmail;
	private String userPhone;
	private String userRegTime;
	private String modyPassTime;
	private String lastLoginTime;
	private String remark;

	private Map<String, String> properties = null;
	transient private Branch branch = null;

	public User() {
	}

	public Long getUserId() {
		return userId;
	}

	public void setUserId(Long userId) {
		this.userId = userId;
	}

	public Long getBrId() {
		return brId;
	}

	public void setBrId(Long brId) {
		this.brId = brId;
	}

	public String getLoginNo() {
		return loginNo;
	}

	public void setLoginNo(String loginNo) {
		this.loginNo = loginNo;
	}

	public String getUserType() {
		return userType;
	}

	public void setUserType(String userType) {
		this.userType = userType;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getUserSex() {
		return userSex;
	}

	public void setUserSex(String userSex) {
		this.userSex = userSex;
	}

	public String getUserStatus() {
		return userStatus;
	}

	public void setUserStatus(String userStatus) {
		this.userStatus = userStatus;
	}

	public String getUserPassword() {
		return userPassword;
	}

	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}

	public String getUserPicture() {
		return userPicture;
	}

	public void setUserPicture(String userPicture) {
		this.userPicture = userPicture;
	}

	public String getUserNation() {
		return userNation;
	}

	public void setUserNation(String userNation) {
		this.userNation = userNation;
	}

	public String getUserPlace() {
		return userPlace;
	}

	public void setUserPlace(String userPlace) {
		this.userPlace = userPlace;
	}

	public String getUserPolity() {
		return userPolity;
	}

	public void setUserPolity(String userPolity) {
		this.userPolity = userPolity;
	}

	public String getUserEmail() {
		return userEmail;
	}

	public void setUserEmail(String userEmail) {
		this.userEmail = userEmail;
	}

	public String getUserPhone() {
		return userPhone;
	}

	public void setUserPhone(String userPhone) {
		this.userPhone = userPhone;
	}

	public String getUserRegTime() {
		return userRegTime;
	}

	public void setUserRegTime(String userRegTime) {
		this.userRegTime = userRegTime;
	}

	public String getModyPassTime() {
		return modyPassTime;
	}

	public void setModyPassTime(String modyPassTime) {
		this.modyPassTime = modyPassTime;
	}

	public String getLastLoginTime() {
		return lastLoginTime;
	}

	public void setLastLoginTime(String lastLoginTime) {
		this.lastLoginTime = lastLoginTime;
	}

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	public int getCachedSize() throws CannotCalculateSizeException {
		int size = 0;
		size += CacheSizes.sizeOfObject(); // overhead of object
		size += CacheSizes.sizeOfLong(); // userId
		size += CacheSizes.sizeOfLong(); // brId
		size += CacheSizes.sizeOfString(loginNo); // loginNo
		size += CacheSizes.sizeOfString(userType); // userType
		size += CacheSizes.sizeOfString(userName); // userName
		size += CacheSizes.sizeOfString(userSex); // userSex
		size += CacheSizes.sizeOfString(userStatus); // userStatus
		size += CacheSizes.sizeOfString(userPassword); // userPassword
		size += CacheSizes.sizeOfString(userPicture); // userPicture
		size += CacheSizes.sizeOfString(userNation); // userNation
		size += CacheSizes.sizeOfString(userPlace); // userPlace
		size += CacheSizes.sizeOfString(userPolity); // userPolity
		size += CacheSizes.sizeOfString(userEmail); // userEmail
		size += CacheSizes.sizeOfString(userPhone); // userPhone
		size += CacheSizes.sizeOfString(userRegTime); // userRegTime
		size += CacheSizes.sizeOfString(modyPassTime); // modyPassTime
		size += CacheSizes.sizeOfString(lastLoginTime); // lastLoginTime
		size += CacheSizes.sizeOfString(remark); // remark
		size += CacheSizes.sizeOfMap(properties);// properties
		return size;
	}

	@Override
	public String toString() {
		return userName;
	}

	/**
	 * 返回当前用户所在的机构
	 * @return
	 * @throws NotFoundException
	 */
	public Branch getBranch() throws NotFoundException {
		synchronized (this) {
			if (branch == null && brId != null) {
				branch = BranchManager.getInstance().getBranch(brId);
			}
		}
		return branch;
	}

	/**
	   * 返回用户的所有扩展属性。用户有任意数量的扩展属性。
	   * 可以修改返回的集合,添加新的属性，或删除现有的。
	   *
	   * @return 用户的所有扩展属性。
	   */
	public Map<String, String> getProperties() {
		synchronized (this) {
			if (properties == null) {
				properties = new ConcurrentHashMap<String, String>();
				loadProperties();
			}
		}
		// 返回一个包装器，用于拦截添加和删除命令。
		return new PropertiesMap();
	}

	private void loadProperties() {
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(LOAD_PROPERTIES);
			pstmt.setLong(1, userId);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next()) {
				properties.put(rs.getString(1), rs.getString(2));
			}
			rs.close();
		} catch (SQLException sqle) {
			Log.error(sqle);
		} finally {
			DbConnectionManager.closeConnection(pstmt, con);
		}
	}

	private void insertProperty(String propName, String propValue) {
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(INSERT_PROPERTY);
			pstmt.setLong(1, userId);
			pstmt.setString(2, propName);
			pstmt.setString(3, propValue);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			Log.error(e);
		} finally {
			DbConnectionManager.closeConnection(pstmt, con);
		}
	}

	private void updateProperty(String propName, String propValue) {
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(UPDATE_PROPERTY);
			pstmt.setString(1, propValue);
			pstmt.setString(2, propName);
			pstmt.setLong(3, userId);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			Log.error(e);
		} finally {
			DbConnectionManager.closeConnection(pstmt, con);
		}
	}

	private void deleteProperty(String propName) {
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(DELETE_PROPERTY);
			pstmt.setLong(1, userId);
			pstmt.setString(2, propName);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			Log.error(e);
		} finally {
			DbConnectionManager.closeConnection(pstmt, con);
		}
	}

	/**
	 * 属性被删除时执行更新数据库.
	 */
	private class PropertiesEntrySet extends AbstractSet {

		public int size() {
			return properties.entrySet().size();
		}

		public Iterator iterator() {
			return new Iterator() {

				Iterator iter = properties.entrySet().iterator();
				Map.Entry current = null;

				public boolean hasNext() {
					return iter.hasNext();
				}

				public Object next() {
					current = (Map.Entry) iter.next();
					return current;
				}

				public void remove() {
					if (current == null) {
						throw new IllegalStateException();
					}
					String key = (String) current.getKey();
					deleteProperty(key);
					iter.remove();
					Map<String, Object> params = new HashMap<String, Object>();
					params.put("type", "propertyDeleted");
					params.put("propertyKey", key);

					//发布事件
					UserEventDispatcher.dispatchEvent(User.this, UserEventDispatcher.EventType.user_modified, params);
				}
			};
		}
	}

	/**
	 * 属性更新时,进行数据库的更新.
	 */
	private class PropertiesMap extends AbstractMap {

		public Object put(Object key, Object value) {
			Map<String, Object> eventParams = new HashMap<String, Object>();
			Object answer;
			String keyString = (String) key;

			synchronized (keyString.intern()) {
				if (properties.containsKey(keyString)) {
					String originalValue = properties.get(keyString);
					answer = properties.put(keyString, (String) value);
					updateProperty(keyString, (String) value);
					eventParams.put("type", "propertyModified");
					eventParams.put("propertyKey", key);
					eventParams.put("originalValue", originalValue);
				} else {
					answer = properties.put(keyString, (String) value);
					insertProperty(keyString, (String) value);
					eventParams.put("type", "propertyAdded");
					eventParams.put("propertyKey", key);
				}
			}

			//发布事件
			UserEventDispatcher.dispatchEvent(User.this, UserEventDispatcher.EventType.user_modified, eventParams);
			return answer;
		}

		public Set<Entry> entrySet() {
			return new PropertiesEntrySet();
		}
	}

}